package baiduai

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gitee.com/xmcommon/mlog"
	"io"
	"io/ioutil"
	"net/http"
	"sync"
	"time"
)

var once sync.Once
var fa FaceRecognition

func NewFaceRecognition() FaceRecognition {
	once = sync.Once{}
	once.Do(func() {
		fa = FaceRecognition{}
		//var err error
		accessToken, err := GetBaiduAccessToken()
		if err != nil {
			mlog.Log.Error(err.Error())
		}
		fa.accessToken = accessToken
		ticker := time.NewTicker(time.Hour * 24 * 10)
		//ticker := time.NewTicker(time.Second * 5)
		go func() {
			for range ticker.C {
				accessToken, err := GetBaiduAccessToken()
				if err != nil {
					mlog.Log.Error(err.Error())
				}
				fa.accessToken = accessToken
			}
		}()

	})
	return fa
}

type FaceRecognition struct {
	accessToken string
}

type reqSearch struct {
	reqRegisterFace
	GroupIdList string `json:"group_id_list"`
	//查找后返回的用户数量。返回相似度最高的几个用户，默认为1，最多返回50个
	MaxUserNum uint32 `json:"max_user_num"`
}

func (f FaceRecognition) Search(image, groupIdList string, maxUserNum uint32) (data map[string]interface{}, err error) {
	r := reqSearch{reqRegisterFace{
		Image:           image,
		ImageType:       "URL",
		QualityControl:  "NONE",
		LivenessControl: "NONE",
	}, groupIdList, maxUserNum}
	url := "https://aip.baidubce.com/rest/2.0/face/v3/search?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)

	m, err := post(url, r)
	if err != nil {
		return
	}
	data = m
	return
}

func (f FaceRecognition) Match(image, image2 string) (data map[string]interface{}, err error) {
	r := []map[string]interface{}{
		{
			"image":            image,
			"image_type":       "URL",
			"face_type":        "LIVE",
			"quality_control":  "NONE",
			"liveness_control": "NONE",
		},
		{
			"image":            image2,
			"image_type":       "URL",
			"face_type":        "LIVE",
			"quality_control":  "NONE",
			"liveness_control": "NONE",
		},
	}
	url := "https://aip.baidubce.com/rest/2.0/face/v3/match?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)

	m, err := post(url, r)
	if err != nil {
		return
	}
	data = m
	return
}

type reqRegisterFace struct {
	Image     string `json:"image"`
	ImageType string `json:"image_type"`
	GroupId   string `json:"group_id,omitempty"`
	UserId    string `json:"user_id,omitempty"`
	UserInfo  string `json:"user_info,omitempty"`
	//图片质量控制
	//NONE: 不进行控制
	//LOW:较低的质量要求
	//NORMAL: 一般的质量要求
	//HIGH: 较高的质量要求
	//默认 NONE
	//若图片质量不满足要求，则返回结果中会提示质量检测失败
	QualityControl string `json:"quality_control,omitempty"`
	//活体检测控制
	//NONE: 不进行控制
	//LOW:较低的活体要求(高通过率 低攻击拒绝率)
	//NORMAL: 一般的活体要求(平衡的攻击拒绝率, 通过率)
	//HIGH: 较高的活体要求(高攻击拒绝率 低通过率)
	//默认NONE
	//若活体检测结果不满足要求，则返回结果中会提示活体检测失败
	LivenessControl string `json:"liveness_control,omitempty"`
	//操作方式
	//APPEND: 当user_id在库中已经存在时，对此user_id重复注册时，新注册的图片默认会追加到该user_id下
	//REPLACE : 当对此user_id重复注册时,则会用新图替换库中该user_id下所有图片
	//默认使用APPEND
	ActionType string `json:"action_type,omitempty,omitempty"`
}

//face_field	否	string	包括age,beauty,expression,face_shape,gender,glasses,landmark,landmark150,race,quality,eye_status,emotion,face_type信息
//逗号分隔. 默认只返回face_token、人脸框、概率和旋转角度
//max_face_num	否	uint32	最多处理人脸的数目，默认值为1，仅检测图片中面积最大的那个人脸；最大值10，检测图片中面积最大的几张人脸。
func (f FaceRecognition) Detect(image, faceField string) (data map[string]interface{}, err error) {
	params := map[string]interface{}{
		"image":            image,
		"image_type":       "URL",
		"face_field":       faceField,
		"max_face_num":     10,
		"face_type":        "LIVE",
		"liveness_control": "NONE",
	}
	url := "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)

	data, err = post(url, params)
	if err != nil {
		return
	}
	//result := data["result"].(map[string]interface{})
	return

}

func (f FaceRecognition) RegisterFace(image, groupID, userID, userInfo string) (data map[string]interface{}, faceToken string, err error) {
	r := reqRegisterFace{
		Image:           image,
		ImageType:       "URL",
		GroupId:         groupID,
		UserId:          userID,
		UserInfo:        userInfo,
		QualityControl:  "NONE",
		LivenessControl: "NONE",
		ActionType:      "REPLACE",
	}
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)

	data, err = post(url, r)
	if err != nil {
		return
	}
	if data["error_code"].(float64) != 0 {
		return
	}
	result := data["result"].(map[string]interface{})
	faceToken = result["face_token"].(string)
	return
}

func (f FaceRecognition) UpdateFace(r reqRegisterFace) (data map[string]interface{}, faceToken string, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/update?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	data, err = post(url, r)
	if err != nil {
		return
	}
	if data["error_code"].(float64) != 0 {
		return
	}
	result := data["result"].(map[string]interface{})
	faceToken = result["face_token"].(string)
	return
}

func (f FaceRecognition) DeleteFace(userId, groupId, faceToken string) (data map[string]interface{}, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/face/delete?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]string)
	params["user_id"] = userId
	params["group_id"] = groupId
	params["face_token"] = faceToken
	data, err = post(url, params)
	if err != nil {
		return
	}
	return
}

//用户组id(由数字、字母、下划线组成，长度限制48B)，如传入“@ALL”则从所有组中查询用户信息。注：处于不同组，但uid相同的用户，我们认为是同一个用户。
func (f FaceRecognition) LookUserInfo(userId, groupId string) (data map[string]interface{}, userInfoM []map[string]string, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/get?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]string)
	params["user_id"] = userId
	params["group_id"] = groupId
	data, err = post(url, params)
	if err != nil {
		return
	}
	if data["error_code"].(float64) != 0 {
		return
	}
	result := data["result"].(map[string]interface{})
	userInfoM = result["user_list"].([]map[string]string)
	return

}
func (f FaceRecognition) GetUserFaceList(userId, groupId string) (data map[string]interface{}, arr []interface{}, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/face/getlist?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]string)
	params["user_id"] = userId
	params["group_id"] = groupId
	data, err = post(url, params)
	if err != nil {
		return
	}
	if data["error_code"].(float64) != 0 {
		return
	}
	result := data["result"].(map[string]interface{})
	arr = result["face_list"].([]interface{})
	return
}

//start	否	uint32	默认值0，起始序号
//length	否	uint32	返回数量，默认值100，最大值1000
func (f FaceRecognition) GetUserList(groupId string, start, length uint32) (data map[string]interface{}, arr []string, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/getusers?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]interface{})
	params["group_id"] = groupId
	params["start"] = start
	params["length"] = length
	data, err = post(url, params)
	if err != nil {
		return
	}
	if data["error_code"].(float64) != 0 {
		return
	}
	arr = data["user_id_list"].([]string)
	return
}

func (f FaceRecognition) CopyUser(userId, srcGroupId, dstGroupId string) (data map[string]interface{}, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/copy?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]interface{})
	params["user_id"] = userId
	params["src_group_id"] = srcGroupId
	params["dst_group_id"] = dstGroupId
	data, err = post(url, params)
	if err != nil {
		return
	}
	return
}

func (f FaceRecognition) DeleteUser(userId, groupId string) (data map[string]interface{}, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/delete?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]interface{})
	params["user_id"] = userId
	params["group_id"] = groupId
	data, err = post(url, params)
	if err != nil {
		return
	}
	return
}

func (f FaceRecognition) CreateUserGroup(groupId string) (data map[string]interface{}, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/add?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]interface{})
	params["group_id"] = groupId
	data, err = post(url, params)
	if err != nil {
		return
	}
	return
}

func (f FaceRecognition) DeleteUserGroup(userId, groupId string) (data map[string]interface{}, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/delete?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]interface{})
	params["group_id"] = groupId
	data, err = post(url, params)
	if err != nil {
		return
	}
	return
}

//start	否	uint32	默认值0，起始序号
//length	否	uint32	返回数量，默认值100，最大值1000
func (f FaceRecognition) GetUserGroupList(start, length uint32) (data map[string]interface{}, arr []string, err error) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/faceset/group/getlist?access_token=%s"
	url = fmt.Sprintf(url, fa.accessToken)
	params := make(map[string]interface{})
	params["start"] = start
	params["length"] = length
	data, err = post(url, params)
	if err != nil {
		return
	}
	if data["error_code"].(float64) != 0 {
		return
	}
	arr = data["group_id_list"].([]string)
	return
}

func GetBaiduAccessToken() (accessToken string, err error) {
	ak := "y0TDmlzNpBtU6Z8Lom6Md9Sv"
	sk := "DHLA0MEEVU8gj4s3MmQxbx7PYS9apel3"
	url := fmt.Sprintf("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s", ak, sk)
	resp, err := http.Get(url)
	if err != nil {
		mlog.Log.Error(err.Error())
		return
	}
	defer resp.Body.Close()
	m := make(map[string]interface{})
	err = bindJson(resp.Body, &m)
	if err != nil {
		mlog.Log.Error(err.Error())
		return
	}
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("http code 500, body %#v", m)
		return
	}
	//logger.Sugar.Infof("%#v", m)
	accessToken = m["access_token"].(string)
	return
}

func bindJson(body io.ReadCloser, v interface{}) (err error) {
	var readAll []byte
	readAll, err = ioutil.ReadAll(body)
	if err != nil {
		return
	}
	err = json.Unmarshal(readAll, &v)
	if err != nil {
		return
	}
	return
}

func post(url string, params interface{}) (m map[string]interface{}, err error) {
	m = make(map[string]interface{})
	marshal, err := json.Marshal(params)
	if err != nil {
		mlog.Log.Error(err.Error())
		return
	}
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(marshal))
	if err != nil {
		mlog.Log.Error(err.Error())
		return
	}
	defer resp.Body.Close()
	err = bindJson(resp.Body, &m)
	if err != nil {
		mlog.Log.Error(err.Error())
		return
	}
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("http code 500, body %+v", m)
		mlog.Log.Error(err.Error())
		return
	}
	return
}

func GeneralFaceUrl(groupId, uid, faceToken string) (faceUrl string) {
	if groupId == "" || uid == "" || faceToken == "" {
		return ""
	}
	return fmt.Sprintf("https://console.bce.baidu.com/ai/s/facelib/face?appId=1180931&groupId=%s&uid=%s&faceId=%s", groupId, uid, faceToken)
}
